
package com.trydofor.id.exec;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.mina.core.session.IoSession;
import org.apache.mina.transport.socket.SocketSessionConfig;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

import com.trydofor.aloe.exception.Exceptions;
import com.trydofor.id.doer.shell.ArgChecker;
import com.trydofor.id.doer.shell.Command;
import com.trydofor.id.doer.shell.Executer;
import com.trydofor.id.doer.shell.Outcome;

/**
 * @author : Shi Rongjiu (www.trydofor.com)
 */
public class SessionExe implements Executer {
    
    private static final DateFormat                  DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
    private final Command                            sessionConfig;
    private final Command                            sessionStatus;
    private final Command                            closeSession;
    
    private final AtomicReference<NioSocketAcceptor> serverReference;
    
    private synchronized String ms(long ms) {
        return DATEFORMAT.format(new Date(ms));
    }
    
    /**
     * constructor
     * 
     * @param server the server
     * @throws NullPointerException if server is null
     */
    public SessionExe(AtomicReference<NioSocketAcceptor> server) {
        Exceptions.checkNullPointer(server);
        this.serverReference = server;
        this.sessionConfig = SessionCmd.sessionConfig();
        this.sessionStatus = SessionCmd.sessionStatus(server);
        this.closeSession = SessionCmd.closeSession(server);
    }
    
    /**
     * {@inheritDoc}
     */
    public Outcome execute(Command command, Map<String, String> argdata) {
        
        String err = ArgChecker.check(command, argdata);
        if (err != null) return new Outcome(-1, null, err);
        
        if (sessionConfig.getAlias().equals(command.getAlias())) {
            return sessionConfig(command, argdata);
        }
        else if (sessionStatus.getAlias().equals(command.getAlias())) {
            return sessionStatus(command, argdata);
        }
        else if (closeSession.getAlias().equals(command.getAlias())) {
            return closeSession(command, argdata);
        }
        return new Outcome(-1, null, "Command is unsupported:" + command);
    }
    
    // ------------------
    
    private Outcome sessionConfig(Command command, Map<String, String> argdata) {
        StringBuilder sb = new StringBuilder();
        int status = 0;
        NioSocketAcceptor server = serverReference.get();
        if (server != null) {
            SocketSessionConfig sc = server.getSessionConfig();
            sb.append("ReceiveBufferSize=").append(sc.getReceiveBufferSize()).append('\n');
            sb.append("SendBufferSize=").append(sc.getSendBufferSize()).append('\n');
            sb.append("SoLinger=").append(sc.getSoLinger()).append('\n');
            sb.append("TrafficClass=").append(sc.getTrafficClass()).append('\n');
            sb.append("KeepAlive=").append(sc.isKeepAlive()).append('\n');
            sb.append("OobInline=").append(sc.isOobInline()).append('\n');
            sb.append("ReuseAddress=").append(sc.isReuseAddress()).append('\n');
            sb.append("TcpNoDelay=").append(sc.isTcpNoDelay()).append('\n');
        }
        else {
            status = -1;
            sb.append("server is null");
        }
        return new Outcome(status, sb.toString(), null);
    }
    
    private Outcome sessionStatus(Command command, Map<String, String> argdata) {
        int status = 0;
        String output = null;
        String errput = null;
        String arg = argdata.get("session-id");
        Long id = Long.valueOf(arg.substring(0, arg.indexOf(":")));
        NioSocketAcceptor server = serverReference.get();
        if (server != null) {
            IoSession session = server.getManagedSessions().get(id);
            if (session == null) {
                status = -2;
                errput = "session not found,session=" + arg;
            }
            else {
                StringBuilder sb = new StringBuilder();
                sb.append("BothIdleCount=").append(session.getBothIdleCount()).append('\n');
                sb.append("CreationTime=").append(ms(session.getCreationTime())).append('\n');
                sb.append("LastBothIdleTime=").append(ms(session.getLastBothIdleTime())).append('\n');
                sb.append("LastIoTime=").append(ms(session.getLastIoTime())).append('\n');
                sb.append("LastReaderIdleTime=").append(ms(session.getLastReaderIdleTime())).append('\n');
                sb.append("LastReadTime=").append(ms(session.getLastReadTime())).append('\n');
                sb.append("LastWriterIdleTime=").append(ms(session.getLastWriterIdleTime())).append('\n');
                sb.append("LastWriteTime=").append(ms(session.getLastWriteTime())).append('\n');
                sb.append("LocalAddress=").append(session.getLocalAddress()).append('\n');
                sb.append("ReadBytes=").append(session.getReadBytes()).append('\n');
                sb.append("ReadBytesThroughput=").append(session.getReadBytesThroughput()).append('\n');
                sb.append("ReaderIdleCount=").append(session.getReaderIdleCount()).append('\n');
                sb.append("ReadMessages=").append(session.getReadMessages()).append('\n');
                sb.append("ReadMessagesThroughput=").append(session.getReadMessagesThroughput()).append('\n');
                sb.append("RemoteAddress=").append(session.getRemoteAddress()).append('\n');
                sb.append("ScheduledWriteBytes=").append(session.getScheduledWriteBytes()).append('\n');
                sb.append("ScheduledWriteMessages=").append(session.getScheduledWriteMessages()).append('\n');
                sb.append("ServiceAddress=").append(session.getServiceAddress()).append('\n');
                sb.append("WriterIdleCount=").append(session.getWriterIdleCount()).append('\n');
                sb.append("WrittenBytes=").append(session.getWrittenBytes()).append('\n');
                sb.append("WrittenBytesThroughput=").append(session.getWrittenBytesThroughput()).append('\n');
                sb.append("WrittenMessages=").append(session.getWrittenMessages()).append('\n');
                sb.append("WrittenMessagesThroughput=")
                  .append(session.getWrittenMessagesThroughput())
                  .append('\n');
                sb.append("BothIdle=").append(session.isBothIdle()).append('\n');
                sb.append("Closing=").append(session.isClosing()).append('\n');
                sb.append("Connected=").append(session.isConnected()).append('\n');
                sb.append("ReaderIdle=").append(session.isReaderIdle()).append('\n');
                sb.append("ReadSuspended=").append(session.isReadSuspended()).append('\n');
                sb.append("WriterIdle=").append(session.isWriterIdle()).append('\n');
                sb.append("WriteSuspended=").append(session.isWriteSuspended()).append('\n');
                
                output = sb.toString();
            }
        }
        else {
            status = -1;
            output = "server is null";
        }
        
        return new Outcome(status, output, errput);
    }
    
    private Outcome closeSession(Command command, Map<String, String> argdata) {
        int status = 0;
        String output = null;
        String errput = null;
        
        String arg = argdata.get("session-id");
        Long id = Long.valueOf(arg.substring(0, arg.indexOf(":")));
        NioSocketAcceptor server = serverReference.get();
        if (server != null) {
            IoSession session = server.getManagedSessions().get(id);
            if (session == null) {
                status = -2;
                errput = "session not found,session=" + arg;
            }
            else {
                boolean imm = Boolean.parseBoolean(argdata.get("immediately"));
                session.close(imm);
                output = "closes session=" + session + ",immediately=" + imm;
            }
        }
        else {
            status = -1;
            output = "server is null";
        }
        return new Outcome(status, output, errput);
    }
}
